<?php

/**
 * @name Trait
 * @author vipkwd <service@vipkwd.com>
 * @link https://github.com/wxy545812093/vipkwd-phputils
 * @license http://www.apache.org/licenses/LICENSE-2.0
 * @copyright The PHP-Tools
 */

declare(strict_types=1);

namespace Vipkwd\Utils\ScanLogin;

use Vipkwd\Utils\Ip as VkIP;
use Vipkwd\Utils\Image\Thumb as VkThumb;
use Vipkwd\Utils\Crypt;
use Vipkwd\Utils\SocketPush\WebPusher;
use Vipkwd\Utils\Http;
use \Vipkwd\Utils\Libs\Net\Request as VKRequest;
use Vipkwd\Utils\ScanLogin\Enum;

trait Traits
{
    private $_request;
    private $_query;
    private $_response;

    private static $_instance = [];
    /**
     * 单例入口
     *
     * @param array $options
     * @return self
     */
    static function instance($options)
    {
        $k = md5(json_encode($options));
        if (!isset(self::$_instance[$k])) {
            self::$_instance[$k] = new self($options);
        }
        return self::$_instance[$k];
    }


    /**
     * 加密：二维码原始数据
     *
     * @param string $event 事件标识
     * @param array $data 二维码原始数据
     * @param int $expireDays <7> 二维码数据源有效期(天数) 0永久有效
     * @param int $expireSeconds <0> 二维码数据源有效期(秒数)，此参数大于0时，参数$expireDays无效
     * @return array[int: days, int: timestamp, string: text]
     */
    private static function createQrcodeData(string $event, array $data, int $expireDays = 7, int $expireSeconds = 0, string $platform = Enum::QR_PLATFORM_PC)
    {

        if ($expireSeconds > 0) {
            $expireDays = -1;
        } else {
            if ($expireDays < 0) {
                $expireDays = 7;
            }
            $expireSeconds = $expireDays * 24 * 3600;
        }
        $expireTimestamps = ($expireSeconds <= 0 ? 0 : time() + $expireSeconds);
        $allowSignKeys = [
            'text2',
            'text',
            Enum::FIXED_KEY_UID,
            Enum::FIXED_KEY_CLIENTID,
            Enum::FIXED_KEY_QRCODEID,
            Enum::FIXED_KEY_NOTIFY,
            Enum::FIXED_KEY_EVENT_NAME,
            Enum::FIXED_KEY_EXPIRES_TIME,
            Enum::FIXED_KEY_CLIENT_IP,
            Enum::FIXED_KEY_IMAGE_CTIME,
            Enum::FIXED_KEY_PLATFORM,
            Enum::FIXED_KEY_STATE,
        ];
        foreach (array_keys($data) as $k) {
            if (!in_array($k, $allowSignKeys)) {
                unset($data[$k]);
            }
        }

        //标记APP 扫码完成且事件鉴定通过后需要上报扫码状态（典型使用场景：扫码后台登录二维码后，需要更新 登录页面的码状态为 “已扫码，请在手机上确认”）
        $data[Enum::FIXED_KEY_NOTIFY] = (isset($data[Enum::FIXED_KEY_NOTIFY]) && $data[Enum::FIXED_KEY_NOTIFY]) ? 1 : 0;

        $data = array_merge($data, [
            Enum::FIXED_KEY_EVENT_NAME => $event,
            Enum::FIXED_KEY_EXPIRES_TIME => $expireTimestamps,
            Enum::FIXED_KEY_CLIENT_IP => VkIP::getClientIp(),
            Enum::FIXED_KEY_IMAGE_CTIME => time(),
            Enum::FIXED_KEY_PLATFORM => in_array($platform, [Enum::QR_PLATFORM_APP, Enum::QR_PLATFORM_OTHER]) ? $platform : Enum::QR_PLATFORM_PC,
        ]);
        \krsort($data);
        return [
            'days' => $expireDays,
            'timestamp' => $expireTimestamps,
            // 'text' => 'ev.' . $event . '|' . Crypt::authcode($data, 'ENCODE', '', $expireSeconds),
            // 'text' => 'ev.' . $event . '|' . Crypt::rc4Encrypt($data, 'ENCODE'),
            //'textRaw' => $data,//'ev.' . $event . '|' . Crypt::aesEncrypt($data, 'ENCODE', '1234567890123456')
            'text' => 'ev.' . $event . '|' . Crypt::aesEncrypt(json_encode($data), Enum::$QR_AES_KEY, Enum::$QR_AES_IV)
        ];
    }

    /**
     * 解码： 二维码扫描结果
     *
     * @param string $text 二维码扫描结果
     * @param string $event 二维码事件验证
     * @return array|string 返回字符串表示验证错误时的行为描述, 解码有效时返回解码数组
     */
    private static function qrcodeScan($data, $event)
    {
        // $text && ($text = Crypt::authcode($text));
        $data && ($data = Crypt::aesDecrypt($data, Enum::$QR_AES_KEY, Enum::$QR_AES_IV));
        if ($data) {
            if (is_array($data) && !empty($data)) {
                if ($data[Enum::FIXED_KEY_EXPIRES_TIME] === 0 || $data[Enum::FIXED_KEY_EXPIRES_TIME] > time()) {
                    if (isset($data[Enum::FIXED_KEY_EVENT_NAME]) && $data[Enum::FIXED_KEY_EVENT_NAME] == $event) {
                        $data['__scan_decode__'] = true;
                        return $data;
                    }
                    return '无效事件';
                }
                return '图码已过期';
            }
        }
        return '图码失效';
    }


    /**
     * 必要参数检测器
     * 
     * @param array $params
     * @param string|array $fields
     * 
     * @return void
     */
    static function paramValidator(array $params = [], $fields = [])
    {
        if (!is_array($fields)) {
            $fields = [$fields];
        }
        $errField = '';
        foreach ($fields as $field) {
            switch (gettype($field)) {
                case "string":
                case "number":
                    if (!isset($params[$field])) {
                        $errField = $field;
                        break 2;
                    }
                    break;
                default:
                    break;
            }
        }
        if ($errField) {
            // header('Content-type: image/jpeg');
            VkThumb::instance()->createPlaceholder("200x200", 1, 1, 12, "{$errField}参数缺失");
            exit;
        }
    }

    /**
     * 生成网址二维码签名
     */
    private static function urlQrcodeSign(array &$parmas = [])
    {
        $params['clientIp'] = VkIP::getClientIp();
        ksort($params);
        return md5(md5(json_encode($params)) . 'urlQrcode' . VkIP::getClientIp());
    }


    /**
     * 推送自定义网页socket通知
     * 
     * @param string $clientId 二维码展示网页监听的 socketId(身份标识)
     * @param string $qrcodeId 展示在网页的那张二维码的ID(身份标识)，源网页接收推送消息的时候应该鉴别qrcodeId
     * @param string $websocketUrl websocket网关地址
     * @param string $event <normal>推送事件标识 [ wscan/confirm/complete/fail/cancel/其他自定义事件标识 ]
     * @param array $data 推送自定义数据
     * 
     * @return boolean
     */
    protected function pushWebMsg(string $clientId, string $qrcodeId, string $event = 'normal', array $data = [])
    {
        $data[Enum::FIXED_KEY_CLIENTID] = $clientId;
        $data[Enum::FIXED_KEY_QRCODEID] = $qrcodeId;
        $eventSupports = [
            'reject',
            Enum::QR_STATE_WSCAN,
            Enum::QR_STATE_SCANED,
            Enum::QR_STATE_CONFIRM,
            Enum::QR_STATE_AUTH_CONFIRM,
            Enum::QR_STATE_AUTH_REJECT,
            Enum::QR_STATE_FAIL,
            Enum::QR_STATE_CANCEL
        ];
        $data['scan_state'] = in_array($event, $eventSupports) ? $event : Enum::QR_STATE_NORMAL;
        $res = WebPusher::instance($this->_options['web_pusher_url'])->to($clientId)->data($data, '', $this->_options['salt_key'])->get();
        return $res == 'ok';
    }


    /**
     * Get Http query object or query param
     * @param string $key
     * @param mixed $default
     * 
     * @return mixed|null|array
     */
    public function query(string $key = null, $default = null)
    {
        $query = array_values((array) $this->_queryVars());
        $query = array_pop($query);
        if ($key) {
            return isset($query[$key]) ? $query[$key] : ($default ? $default : null);
        }
        return $query;
    }

    /**
     * Get Http request Object or request param
     * 
     * @param string $key
     * @param mixed $default
     * 
     * @return mixed|null|VKRequest
     */
    public function request(string $key = null, $default = null)
    {
        if ($key) {
            if ($this->_requestVars()->$key === null) {
                return $default !== null ? $default : null;
            }
            return $this->_requestVars()->$key;
        }
        return $this->_requestVars();
    }
    public function response()
    {
        if (!$this->_response)
            $this->_response = Http::response();
        return $this->_response;

    }
    private function _requestVars(): VKRequest
    {
        if (!$this->_request)
            $this->_request = Http::request();
        return $this->_request;
    }
    private function _queryVars()
    {
        if (!$this->_query)
            $this->_query = $this->_requestVars()->query;
        return $this->_query;
    }
}
